package com.dxy.hms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.corundumstudio.socketio.SocketIOServer;
import com.dxy.hms.auth.LoginUser;
import com.dxy.hms.base.StatusMessageEnum;
import com.dxy.hms.base.UnifiedException;
import com.dxy.hms.model.entity.User;
import com.dxy.hms.model.entity.UserRole;
import com.dxy.hms.manager.UserCacheManager;
import com.dxy.hms.mapper.RoleMapper;
import com.dxy.hms.mapper.UserMapper;
import com.dxy.hms.mapper.UserRoleMapper;
import com.dxy.hms.model.dto.UpdPwdDto;
import com.dxy.hms.model.dto.UserDto;
import com.dxy.hms.model.vo.UserVo;
import com.dxy.hms.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dxy.hms.utils.LoginUtil;
import com.dxy.hms.utils.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author dxy
 * @since 2024-09-23
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserCacheManager userCacheManager;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;

    @Override
    @Transactional
    public void add(User entity) {
        String account = entity.getAccount();
        String password = entity.getPassword();
        Assert.notBlank(account, "账号不能为空");
        Assert.notBlank(password, "密码不能为空");
        // 验证重名
        Wrapper<User> wrapper = new LambdaQueryWrapper<User>().eq(User::getAccount, account);
        if (baseMapper.selectCount(wrapper) > 0) {
            throw new UnifiedException(StatusMessageEnum.ACCOUNT_EXISTED);
        }

        // 加密密码
        entity.setPassword(MD5Util.getEncryptPassword(password));
        baseMapper.insert(entity);

        // 保存用户角色关系
        if (entity instanceof UserDto) {
            UserDto userDto = (UserDto) entity;
            handleUserRole(entity.getId(), userDto.getRoleIdList());
        }
    }

    private void handleUserRole(Long userId, List<Long> roleIdList) {
        if (CollUtil.isEmpty(roleIdList)) {
            // 删除旧的关联关系
            userRoleMapper.delByUserId(userId);
        } else {
            // 保存新的关联关系
            List<UserRole> userRoleList = roleIdList.stream()
                    .map(roleId -> new UserRole().setUserId(userId).setRoleId(roleId)).collect(Collectors.toList());
            userRoleMapper.addOrIgnoreBatch(userRoleList);

            // 删除旧的且不在新关联列表中的关联关系
            userRoleMapper.delByUserIdAndNotInRoleIds(userId, roleIdList);
        }
    }

    @Override
    @Transactional
    public void updById(User entity) {
        Assert.notNull(entity.getId(),"id不能为空");
        // 账号和密码不能更新
        entity.setPassword(null);
        entity.setAccount(null);
        baseMapper.updateById(entity);

        // 保存用户角色关系
        if (entity instanceof UserDto){
            UserDto userDto = (UserDto)entity;
            handleUserRole(entity.getId(), userDto.getRoleIdList());
        }

        // TODO 删除缓存，此处可以优化成解耦形式
        userCacheManager.removeById(entity.getId());
    }

    @Override
    @Transactional
    public void delByIds(List<Long> ids) {
        // 删除用户
        baseMapper.deleteBatchIds(ids);
        // 删除用户和角色关联关系
        ids.forEach(id -> userRoleMapper.delByUserId(id));
        // TODO 删除缓存，此处可以优化成解耦形式
        ids.forEach(id -> userCacheManager.removeById(id));
    }

    @Override
    public UserVo getDetail(Long id) {
        User user = baseMapper.selectById(id);
        if (user != null) {
            return toUserVo(user);
        }
        return null;
    }

    @Override
    public IPage<UserVo> getPage(String usernameKeywords, Long pageNum, Long pageSize) {
        if (pageNum == null || pageNum <= 0) {
            pageNum = 1L;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 10L;
        }
        IPage<User> page = new Page<>(pageNum, pageSize);
        Wrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .like(StrUtil.isNotEmpty(usernameKeywords), User::getAccount, usernameKeywords)
                .orderByDesc(User::getCreateTime);
        IPage pageList = baseMapper.selectPage(page, wrapper);
        List<User> records = pageList.getRecords();
        if (CollUtil.isNotEmpty(records)) {
            List<UserVo> voList = records.stream().map(this::toUserVo).collect(Collectors.toList());
            pageList.setRecords(voList);
        }
        return pageList;
    }

    private UserVo toUserVo(User user){
        UserVo userVo = BeanUtil.toBean(user, UserVo.class);
        userVo.setRoleList(roleMapper.getRoleByUserId(user.getId()));
        return userVo;
    }

    @Override
    public void updPwd(UpdPwdDto dto) {
        if (StrUtil.isBlank(dto.getOldPwd()) || StrUtil.isBlank(dto.getNewPwd())) {
            String paramTip = "oldPwd=" + dto.getOldPwd() + ", newPwd=" + dto.getNewPwd();
            throw new UnifiedException(StatusMessageEnum.ILLEGAL_ARGUMENT_ERROR, paramTip);
        }
        LoginUser loginUser = LoginUtil.getLoginUserOrException();
        User dbUser = baseMapper.selectById(loginUser.getUserId());
        if (dbUser == null) {
            throw new UnifiedException(StatusMessageEnum.ACCOUNT_NOT_EXIST);
        }


        if (!Objects.equals(MD5Util.getEncryptPassword(dto.getOldPwd()), dbUser.getPassword())) {
            throw new UnifiedException(StatusMessageEnum.PWD_ERROR);
        }

        baseMapper.updateById(new User().setId(loginUser.getUserId()).setPassword(MD5Util.getEncryptPassword(dto.getNewPwd())));

    }

    @Override
    public UserVo getLoginUserInfo() {
        LoginUser loginUser = LoginUtil.getLoginUserOrException();
        return getDetail(loginUser.getUserId());
    }

}
